<?php
namespace app\admin\service;

use think\facade\Cookie;
use app\admin\model\Admin;
use app\admin\exception\AuthException;
use think\Validate;
use app\admin\validate\CreateAdmin;
use app\admin\validate\UpdateAdmin;

/**
 * 用户与用户授权相关.
 * XXX: 或许可以把这个类实例化...然后用容器绑定实例...
 */
class Auth
{
   /**
     * 当前登录的用户
     * @var Admin|null
     */
    private static $currentAdmin;

    /**
     * 检测用户是否登录.
     * @return bool
     */
    public static function check()
    {
        try {
            return (bool) self::admin();
        } catch (AuthException $e) {
            return false;
        } catch (\Exception $e) {
            self::logout();
            return false;
        }
    }

    /**
     * 返回已经登录的用户对象
     * @return Admin
     * @throws AuthException 如果用户未登录, 抛出异常
     */
    public static function admin()
    {
        if (empty(self::$currentAdmin)) {
            $token = self::getCurrentAdminToken();
            if ($token) {
                $admin = Admin::findByToken($token);
                if ($admin) {
                    self::$currentAdmin = $admin;
                } else {
                    throw new AuthException("无法获取当前用户");
                }
            } else {
                throw new AuthException("用户未登录!");
            }
        }

        return self::$currentAdmin;
    }

    /**
     * 设置当前登录的用户
     * @param Admin $admin
     * @param bool $remember 是否记住登录
     * @return null
     */
    public static function setCurrentLoginAdmin(Admin $admin, $remember = false)
    {
        self::$currentAdmin = $admin;
        $admin->loginSuccess();
        $token = $admin->getRememberToken();
        if ($remember) {
            Cookie::set('user_token', $token, 3600 * 24 * 7);
        } else {
            Cookie::set('user_token', $token, 3600 * 24);
        }
    }

    /**
     * @return int|null 尝试获取当前用户的token
     */
    public static function getCurrentAdminToken()
    {
        // 值不存在时返回 null
        return Cookie::get('user_token');
    }

    /**
     * @param string $password 输入的密码明文.
     * @return string hash 后的密码.
     */
    private static function getHashedPassword($password)
    {
        // PASSWORD_BCRYPT 算法总是返回一个 60 字符的 hash 字符串, 但是原始密码超过 72 字符时将会被截断
        return password_hash($password, PASSWORD_BCRYPT);
    }

    /**
     * 使用 用户名 和密码登录.
     * @param array $data 包含账号密码的数据.
     * @return Admin
     * @throws AuthException
     */
    public static function login($data)
    {
        $validate = new Validate([
            'username' => 'require',
            'password' => 'require'
        ]);
        if (!$validate->check($data)) {
            throw new AuthException($validate->getError());
        }

        // 尝试登陆的账户数据
        $account = [
            'username' => $data['username']
        ];

        $admin = Admin::get($account);
        if ($admin && password_verify($data['password'], $admin->password) === true) {
            if (!$admin->isAccountEnabled()) {
                throw new AuthException("该账号已经被禁用!");
            }
            $remember = isset($data['is_remember']) ? $data['is_remember'] : false;
            self::setCurrentLoginAdmin($admin, $remember);
            return $admin;
        } elseif ($admin) {
            $admin->loginFailed();
            // 如果账号存在, 则记录登陆错误次数
            throw new AuthException('账号或密码错误');
        } else {
            throw new AuthException('账号或密码错误');
        }
    }

    /**
     * 登出当前用户.
     */
    public static function logout()
    {
        Cookie::clear(config('cookie.prefix'));
    }

    /**
     * 创建账号.
     * @param array $data
     * @return Admin
     * @throws AuthException
     */
    public static function create($data)
    {
        $validate = new CreateAdmin();
        if (!$validate->check($data)) {
            throw new AuthException($validate->getError());
        }
        // 只提取必要的字段
        $data = [
            'username' => $data['username'],
            'email' => $data['email'],
            'nickname' => isset($data['nickname']) ? $data['nickname'] : '',
            'avatar' => isset($data['avatar']) ? $data['avatar'] : '',
            // 默认是启用账号
            'status' => isset($data['status']) ? $data['status'] : Admin::STATUS_NORMAL,
            'site_ids' => isset($data['site_ids']) ? $data['site_ids'] : [],
            'role_ids' => isset($data['role_ids']) ? $data['role_ids'] : [],
            'manage_all' => isset($data['manage_all']) ? $data['manage_all'] : 0,
            'password' => self::getHashedPassword($data['password']),
        ];
        $admin = new Admin();
        $admin->data($data, true);
        $admin->save();
        return $admin;
    }

    /**
     * 更新用户信息
     * @param Admin $admin 被更新的用户对象
     * @param array $data 新数据
     * @param Admin $editor 编辑者
     * @return Admin
     */
    public static function update(Admin $admin, $data, Admin $editor = null)
    {
        $validate = new UpdateAdmin();
        if (!$validate->check($data)) {
            throw new AuthException($validate->getError());
        }

        if (isset($data['password']) && $data['password']) {
            $data['password'] = self::getHashedPassword($data['password']);
        } else {
            // 如果密码为空, 必须unset这个字段, 否则数据库中的字段也会变为空字符串
            unset($data['password']);
        }

        $allowFields = [
            'email', 'nickname', 'password', 'avatar',
            'status', 'manage_all', 'site_ids', 'role_ids'
        ];

        // 限制写入字段
        $admin->allowField($allowFields)->save($data);
        $admin->allowField(true);
        return $admin;
    }

    /**
     * 删除用户, 删除用户后, 将用户发的帖子和回复标记为删除.
     * @param int $id 用户id
     * @return null
     */
    public static function delete($id)
    {
        Admin::destroy($id);
    }
}
